/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package problema;

import java.util.LinkedList;
import java.util.List;
import schema.Coordinate;
import schema.Disuguaglianze;
import schema.Schema;
import schema.Simbolo;

/**
 *
 * @author Antonio
 */
public class FutoshikiSolver extends Problema<Coordinate, Integer> {

    private Schema schema;
    private List<int[][]> soluzioni;// la chiave e' data da: solTrovate
    private Coordinate primoPuntoDiScelta;
    private Coordinate ultimoPuntoDiScelta;
    private List<Coordinate> valInput;

    public FutoshikiSolver(int numSoluzioni, Schema schema) {

        this.schema = schema;
        this.numSoluzioni = numSoluzioni;
        this.soluzioni = new LinkedList();

        valInput = new LinkedList();
        listInput();

        solTrovate = 0;

        if (schema.getVal(new Coordinate(0, 0)) == 0) {

            primoPuntoDiScelta = new Coordinate(0, 0);
        } else {

            primoPuntoDiScelta = prossimoPuntoDiScelta(new Coordinate(0, 0));
        }

        if (schema.getVal(new Coordinate(schema.GRIGLIA - 1, schema.GRIGLIA - 1)) == 0) {

            ultimoPuntoDiScelta = new Coordinate(schema.GRIGLIA - 1, schema.GRIGLIA - 1);
        } else {

            ultimoPuntoDiScelta = precedentePuntoDiScelta(new Coordinate(schema.GRIGLIA - 1, schema.GRIGLIA - 1));
        }
    }

    private void listInput() {

        for (int i = 0; i < schema.getMatrice().length; i++) {

            for (int j = 0; j < schema.getMatrice().length; j++) {

                if (schema.getVal(new Coordinate(i, j)) != 0) {

                    valInput.add(new Coordinate(i, j));
                }
            }
        }
    }

    public List<int[][]> getSoluzioni() {
        return soluzioni;
    }

    @Override
    protected Coordinate primoPuntoDiScelta() {

        return primoPuntoDiScelta;
    }

    @Override
    protected Coordinate prossimoPuntoDiScelta(Coordinate ps) {

        int riga = 0;
        int colonna = 0;

        if (ps.getColonna() == schema.GRIGLIA - 1) {

            riga = ps.getRiga() + 1;
            colonna = 0;
        } else {

            riga = ps.getRiga();
            colonna = ps.getColonna() + 1;
        }

        Coordinate c = new Coordinate(riga, colonna);

        for (Coordinate in : valInput) {

            if (in.equals(c)) {

                return prossimoPuntoDiScelta(c);
            }
        }
        return c;
    }

    @Override
    protected Coordinate ultimoPuntoDiScelta() {

        return ultimoPuntoDiScelta;
    }

    @Override
    protected Integer primaScelta(Coordinate ps) {

        return 1;
    }

    @Override
    protected Integer prossimaScelta(Integer s) {

        return s + 1;
    }

    @Override
    protected Integer ultimaScelta(Coordinate ps) {

        return schema.GRIGLIA;
    }

    @Override
    protected boolean assegnabile(Integer scelta, Coordinate puntoDiScelta) {

        return cond1(scelta, puntoDiScelta) && cond2(scelta, puntoDiScelta);
    }

    private boolean cond1(Integer s, Coordinate p) {
        /*
         * Condizione da osservare:
         * 
         * Il parametro s non deve essere gia' presente 
         * sulla stessa riga o colonna.
         */

        boolean assegnabile = true;
        int i = 0;

        while (i < schema.GRIGLIA && assegnabile) {// controllo sulla riga

            if (schema.getVal(new Coordinate(p.getRiga(), i)) == s) {

                assegnabile = false;
            }

            i++;
        }
        i = 0;
        while (i < schema.GRIGLIA && assegnabile) {// controllo sulla colonna

            if (schema.getVal(new Coordinate(i, p.getColonna())) == s) {

                assegnabile = false;
            }

            i++;
        }
        return assegnabile;
    }

    private boolean cond2(Integer s, Coordinate p) {
        /*
         * Condizione da osservare:
         * 
         * Se sulla coordinata p lo schema presenta una disequazione
         * s deve osservare tale vincolo.
         */

        if (schema.getSimboli().isEmpty()) {

            return true;
        } else {

            for (Simbolo si : schema.getSimboli()) {

                if (si.getCoordinataA().equals(p)) {

                    if (si.getDisuguaglianza().equals(Disuguaglianze.GT)) {// simbolo >

                        return schema.getVal(si.getCoordinataB()) == 0 || s > schema.getVal(si.getCoordinataB());

                    } else if (si.getDisuguaglianza().equals(Disuguaglianze.LT)) {// simbolo <

                        return schema.getVal(si.getCoordinataB()) == 0 || s < schema.getVal(si.getCoordinataB());
                    }

                    return true;
                } else if (si.getCoordinataB().equals(p)) {

                    if (si.getDisuguaglianza().equals(Disuguaglianze.GT)) {// simbolo >

                        return schema.getVal(si.getCoordinataA()) == 0 || s < schema.getVal(si.getCoordinataA());

                    } else if (si.getDisuguaglianza().equals(Disuguaglianze.LT)) {// simbolo <

                        return schema.getVal(si.getCoordinataA()) == 0 || s > schema.getVal(si.getCoordinataA());
                    }

                    return true;
                }
            }

            return true; // simbolo senza disuguaglianza
        }
    }

    @Override
    protected void assegna(Integer scelta, Coordinate puntoDiScelta) {

        schema.addValore(scelta, puntoDiScelta);
    }

    @Override
    protected void deassegna(Integer scelta, Coordinate puntoDiScelta) {

        schema.addValore(0, puntoDiScelta);
    }

    @Override
    protected Coordinate precedentePuntoDiScelta(Coordinate ps) {

        int riga = 0;
        int colonna = 0;

        if (ps.getColonna() == 0) {

            riga = ps.getRiga() - 1;
            colonna = schema.GRIGLIA - 1;
        } else {

            riga = ps.getRiga();
            colonna = ps.getColonna() - 1;

        }

        Coordinate c = new Coordinate(riga, colonna);

        for (Coordinate in : valInput) {

            if (in.equals(c)) {

                return precedentePuntoDiScelta(c);
            }
        }
        return c;
    }

    @Override
    protected Integer ultimaSceltaAssegnataA(Coordinate puntoDiScelta) {

        return schema.getVal(puntoDiScelta);
    }

    @Override
    protected void scriviSoluzione(int numSol) {

        soluzioni.add(copia());
    }

    private int[][] copia() {

        int[][] matrice = new int[schema.GRIGLIA][schema.GRIGLIA];

        for (int i = 0; i < schema.getMatrice().length; i++) {

            for (int j = 0; j < schema.getMatrice().length; j++) {

                matrice[i][j] = schema.getVal(new Coordinate(i, j));
            }
        }
        return matrice;
    }
}